/**
 * 
 */
package com.ms.game.lobby;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ms.game.common.OSType;
import com.ms.game.common.container.BasicSetting;
import com.ms.game.common.container.BasicSettingUtil;
import com.ms.game.common.container.GameTypeInfo;
import com.ms.game.gameActivity.db.GameActivitySettingModel;
import com.ms.game.gmtool.WhiteListInfo;
import com.ms.game.lobby.bulletin.BullentinAllListInfo;
import com.ms.game.lobby.bulletin.BulletinInfo;
import com.ms.game.lobby.bulletin.BulletinListInfo;
import com.ms.game.lobby.bulletin.BulletinModelInfo;
import com.ms.game.lobby.bulletin.BulletinTitleInfo;
import com.ms.game.lobby.db.BulletinDAO;
import com.ms.game.lobby.db.GameLobbyJackpotNotifyLogModel;
import com.ms.game.lobby.message.CLobbyHallMachinedataRequest;
import com.ms.game.lobby.message.CPlayerModifyOfflineKeepDataRequest;
import com.ms.game.lobby.message.GameActivitySettings;
import com.ms.game.lobby.message.JackpotEventType;
import com.ms.game.lobby.message.SGetAutoRecoverLuckyCoinResponse;
import com.ms.game.lobby.message.SHallTypeData;
import com.ms.game.lobby.message.SJackpotNotify;
import com.ms.game.lobby.message.SLobbyHallListResponse;
import com.ms.game.lobby.message.SPlayerCheckOfflineKeepDataResponse;
import com.ms.game.lobby.message.SPlayerModifyOfflineKeepDataResponse;
import com.ms.game.lobby.message.SimpleKeepData;
import com.ms.game.mgdh.message.SDoorManCancelOfflineKeepDataBackRequest;
import com.ms.game.mgdh.message.SDoorManCancelOfflineKeepDataRequest;
import com.ms.game.offlineSpin.async.LoadOfflineSpinResultAsync;
import com.ms.game.slot.base.GainRateData;
import com.ms.game.slot.base.GainRateSystem;
import com.ms.game.slot.base.message.CSlotCommonDmSetRecvSeatUpdatePageRequest;
import com.ms.player.Player;
import com.ms.player.PlayerNickNameParser;
import com.ms.service.error.ErrorCode;
import com.ms.service.module.analysis.AnalysisProcessor;
import com.ms.service.module.analysis.PlayerEnterSeatStatusType;
import com.ms.service.module.datacenterlite.GameGetKeepSeatAyncCommand;
import com.ms.service.module.datacenterlite.GameKeepSeatData;
import com.ms.service.module.datacenterlite.constant.DCLiteMessageType;
import com.ms.service.module.zone.BasicZone;
import com.ms.service.module.zone.db.GameHallConfigModel;
import com.ms.service.module.zone.db.GameHallDAO;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.datamanager.BaseDataInfo;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoin;
import com.xpec.c4.game.datamanager.agent.money.ChangeCoinLister;
import com.xpec.c4.game.datamanager.agent.money.CoinType;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeParam;
import com.xpec.c4.game.datamanager.agent.money.MoneyChangeWay;
import com.xpec.c4.game.system.GMToolApiMultiModuleDAO;
import com.xpec.c4.game.time.TimeConstants;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GUIDFactory;
import com.xpec.c4.game.utility.GUIDType;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.contexts.Contexts;
import com.xpec.c4.service.group.transportlayer.BroadcastTargetTypes;
import com.xpec.c4.service.group.transportlayer.GroupMessage;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.common.PlayerCommonInfoManager;

/**
 * 遊戲大廳的資料管理物件
 * 
 * @author TW15010
 */
public class GameLobbyProcesser extends BasicZone {
	
	@SuppressWarnings("unused")
	private static final Log log = LogFactory.getLog(GameLobbyProcesser.class);
	
	private HashMap<OSType, HashMap<Integer, GameHallConfigModel>> gameList = null;
	
	/** 館/設定model*/
	private HashMap<String,HashMap<String, GameActivitySettingModel>> hallActivitySettings = new HashMap<String, HashMap<String,GameActivitySettingModel>>();

	/** 自動回復幸運金的回復上限 */
	private static long AUTO_RECOVER_LUCKY_COIN_MAX = 60;
	
	/**
	 * SLOT GAME 
	 */
	private static final int  GAME_SLOT = 0;
	
	/**
	 * 牌桌類遊戲
	 */
	private static final int GAME_TABLE = 1;

	/**
	 * 發送中獎通知給所有的 client
	 * 
	 * @param module
	 *            觸發事件的 module
	 * @param player
	 *            觸發事件的 player
	 * @param eventType
	 *            中獎事件類型，參考 {@link JackpotEventType}
	 * @param gameName
	 *            遊戲名稱
	 * @param hallName
	 *            遊戲館名
	 * @param bonusName
	 *            獎項名稱
	 * @param coin
	 *            獲得金幣
	 * @param remainValue
	 *            餘額
	 */
	public static void handleBroadcastJackpotNotify(BasicServiceModule module, Player player, int eventType,
			String gameName, String hallName, String bonusName, long coin, double remainValue) {
		// 發送中獎通知給所有的 client
		sendJackpotNotify(player, eventType, gameName, hallName, bonusName, coin, remainValue);

		// 送出之後還要寫入 log db
		GameLobbyJackpotNotifyLogModel model = new GameLobbyJackpotNotifyLogModel();

		/** 事件類型 */
		model.setEventType(eventType);

		/** 玩家名稱 */
		model.setPlayerName(player.getName());

		/** 遊戲名稱 */
		model.setGameName(gameName);

		/** 遊戲館名 */
		model.setHallName(hallName);

		/** 獎項名稱 */
		model.setBonusName(bonusName);

		/** 獲得金幣 */
		model.setCoin(coin);

		/** 餘額 */
		model.setRemainValue(new BigDecimal(remainValue));

		// 寫入 log
		//DbLogManager.saveLog(module, player.getGuid(), model);
		//DbLogManager.savaLog(model);
		
		
		module.saveDbLog(model);
	}

	/**
	 * 處理領取自動回復幸運金的要求
	 * 
	 * @param player
	 *            player data
	 */
	public static void handleGetAutoRecoverLuckyCoin(Player player) {
		if (player == null) {
			return;
		}
		
		GameLobbyModule module = (GameLobbyModule) Contexts.getModule();
		GameLobbyProcesser processor = (GameLobbyProcesser) module.getZone();

		// 上次領取的時間
		long lastGetTime = player.getLastGetLuckyCoinTime();
		long lastGetTimeMinute = GameUtility.divisible(lastGetTime, TimeConstants.MILLI_SECOND_PRE_MINUTE);

		// 目前的時間
		long curTime = TimeSys.currentTimeMills();
		long curTimeMinute = GameUtility.divisible(curTime, TimeConstants.MILLI_SECOND_PRE_MINUTE);
		
		// 每分鐘 1 個幸運金
		long luckyCoin = curTimeMinute - lastGetTimeMinute;
		BigDecimal luckyCoinBig = new BigDecimal(luckyCoin);
		if (Helper.isZero(luckyCoinBig)) {
			sendGetAutoRecoverLuckyCoinResponse(player.getSessionID(), ErrorCode.LUCKY_COIN_NO_COIN_CAN_GET,
					luckyCoinBig);
		}
		
		if(luckyCoin > AUTO_RECOVER_LUCKY_COIN_MAX){
			luckyCoin = AUTO_RECOVER_LUCKY_COIN_MAX;
		}

		ChangeCoinLister coinLister = new ChangeCoinLister();
		coinLister.addCoin(CoinType.LUCKY, luckyCoinBig);
		
		module.getPlayerDataAgent().updatePlayerMoneyToDC(GUIDFactory.createUUID(GUIDType.TYPE_GET_LUCKY_COIN),
				player, coinLister, MoneyChangeWay.ADD_GET_AUTO_RECOVER_LUCKY_COIN, MoneyChangeParam.NO_JACKPOT,
				false, null, null, module, "", "", ModuleName.GAMELOBBY, "", null, (Object) processor,
				"onGetAutoRecoverLuckyCoinToDcCallback");
	}

	/**
	 * 領取自動回復幸運金從 dc 回來的 callback
	 * 
	 * @param obj
	 * @param userObject
	 */
	public void onGetAutoRecoverLuckyCoinToDcCallback(BaseDataInfo obj, Object... userObject) {
		MoneyChangeParam param = (MoneyChangeParam) obj.getData();

		Player player = getPlayer(param.getPlayerId());
		if (player == null) {
			return;
		}

		BigDecimal luckyCoin = BigDecimal.ZERO;
		if (param.getResult() == ErrorCode.SUCCESS) {
			ChangeCoin coin = param.getChangeCoinLister().getCoinMap().get(CoinType.LUCKY);
			if (coin != null) {
				luckyCoin = coin.getChangeValue();
			}
		}

		sendGetAutoRecoverLuckyCoinResponse(player.getSessionID(), param.getResult(), luckyCoin);
	}

	/**
	 * 傳送領取自動回復幸運金的結果給 client
	 * 
	 * @param sessionId
	 *            player session id
	 * @param errorCode
	 *            執行結果代碼
	 * @param luckyCoin
	 *            獲得的幸運金金額
	 */
	public static void sendGetAutoRecoverLuckyCoinResponse(GUID sessionId, int errorCode, BigDecimal luckyCoin) {
		if (sessionId == null) {
			return;
		}

		SGetAutoRecoverLuckyCoinResponse data = new SGetAutoRecoverLuckyCoinResponse();
		data.setErrorCode(errorCode);
		data.setLuckyCoin(luckyCoin.toString());

		MessageAction.sendMessage(sessionId, MessageTypes.S_LOBBY_GET_AUTO_RECOVER_LUCKY_COIN_RESPONSE, data);
	}

	/**
	 * 變更玩家名字後向 dc 要求更新 player 資料的 callback
	 * 
	 * @param success
	 *            執行結果
	 * @param sessionId
	 *            player session id
	 */
	public static void onChangeNameUpdatePlayerDataToDCCallback(Boolean success, GUID sessionId) {
		if (success) {
			sendChangeNameResponse(sessionId, ErrorCode.SUCCESS);
		} else {
			sendChangeNameResponse(sessionId, ErrorCode.DB_OPERATION_FAIL);
		}
	}

	/**
	 * 回傳改變角色名稱的結果
	 * 
	 * @param sessionId
	 *            client session id
	 * @param code
	 *            result code
	 */
	public static void sendChangeNameResponse(GUID sessionId, int code) {
		MessageAction.sendMessage(sessionId, MessageTypes.S_LOBBY_CHANGE_NAME_RESPONSE, code);
	}

	/**
	 * 發送中獎通知給所有的 client
	 * 
	 * @param player
	 *            觸發事件的 player
	 * @param eventType
	 *            中獎事件類型，參考 {@link JackpotEventType}
	 * @param gameName
	 *            遊戲名稱
	 * @param hallName
	 *            遊戲館名
	 * @param bonusName
	 *            獎項名稱
	 * @param coin
	 *            獲得金幣
	 * @param remainValue
	 *            餘額
	 */
	public static void sendJackpotNotify(Player player, int eventType, String gameName, String hallName,
			String bonusName, long coin, double remainValue) {
		SJackpotNotify notify = new SJackpotNotify();

		// 事件類型
		notify.setEventType(eventType);

		// 發生時間
		notify.setEventTime(TimeSys.currentTimeMills());

		// 玩家名稱
		notify.setPlayerName(PlayerNickNameParser.mergeNickName(player.getName(), player.getNameNumber()));

		// 遊戲名稱
		notify.setGameName(gameName);

		// 遊戲館名
		notify.setHallName(hallName);

		// 獎項名稱
		notify.setBonusName(bonusName);

		// 獲得金幣
		notify.setCoin(coin);

		// 餘額
		notify.setRemainValue(String.valueOf(remainValue));

		// 傳送給 client
		MessageAction.broadCastMessage(MessageTypes.S_LOBBY_JACKPOT_NOTIFY, notify , 
				BroadcastTargetTypes.BROADCAST_TO_ALL);
	}
	
	
	/**
	 * 快取遊戲列表資料
	 */
	public void onCacheGameTypeData(String resultKey)
	{
		boolean haveResultKey = GameUtility.isHaveString(resultKey);
		
		BasicServiceModule curModule = Contexts.getModule();
		
		gameList = new HashMap<OSType, HashMap<Integer,GameHallConfigModel>>();
		
		try {
			ArrayList<GameHallConfigModel> models = (ArrayList<GameHallConfigModel>) GameHallDAO.getHallConfigList();
			for (GameHallConfigModel m : models) {
				OSType osType = m.getOsType();
				HashMap<Integer, GameHallConfigModel> list = gameList.get(osType);
				if(list == null){
					list = new HashMap<Integer, GameHallConfigModel>();
					gameList.put(osType, list);
				}
				
				list.put(m.getOrderNum(), m);
			}
		} catch (Exception e) {
			if (haveResultKey) {
				GMToolApiMultiModuleDAO.setProcessResult(curModule.getModuleName(), resultKey, false, e.getClass()
						.getSimpleName());
			}
			return;
		}

		if (haveResultKey) {
			//回Gmtool
			GMToolApiMultiModuleDAO.setProcessResult(curModule.getModuleName(), resultKey, true, null); 
			
			//廣播給在gamelobby的人
			onBroadCastGameList();
		}
	}
	
	/**
	 * 廣播遊戲列表給在大廳的人
	 */
	private void onBroadCastGameList(){
		HashMap<GUID, Player> playerList = getPlayerList();
		
		if (GameUtility.getMapSize(playerList) <= 0) { //沒有人
			return;
		}
		
		GameListWhileGroup group = ((GameLobbyModule)Contexts.getModule()).getWhileGroup();
		
		for (Entry<GUID, Player> playerMap : playerList.entrySet()) {
			GUID guid = playerMap.getKey();
			Player player = playerMap.getValue();

			// 取Session
			GUID sessionId = PlayerCommonInfoManager.getInstance().getSessionByPlayerGuid(guid);
			if (sessionId == null) { // 不在線上跳過
				continue;
			}
			
			OSType osType = player.getOsType();
			if(osType == null){
				continue;
			}
			
			// 取IP
			String playerIP = PlayerCommonInfoManager.getInstance().getClientIpBySessionId(sessionId);

			SLobbyHallListResponse response = getHallTypeList(group, guid.toString(), playerIP, osType);
			MessageAction.sendMessage(sessionId, MessageTypes.S_LOBBY_BROADCAST_HALLTYPELIST_RESPONSE, response);
		}		
	}	
	
	
	/**
	 * 組成遊戲列表要的response
	 * @param group
	 * @param playerId
	 * @param playerIP
	 * @param osType
	 * @return
	 */
	private SLobbyHallListResponse getHallTypeList(GameListWhileGroup group,String playerId, String playerIP, OSType osType ){
		SLobbyHallListResponse response = new SLobbyHallListResponse();
		boolean inWhiteList = group.hasWhile(playerId);

		HashMap<Integer, GameHallConfigModel> list = (osType == null) ? null : gameList.get(osType);
		
		if(list != null){
			
			TreeSet<Integer> keySet = new TreeSet<Integer>(list.keySet());
			
			for (Integer order : keySet) {
				
				GameHallConfigModel model = list.get(order);
				
//				//只提供與玩家OS相同的列表
//				if(osType != null && osType != model.getOsType()){
//					continue;
//				}
				
				SHallTypeData hall = new SHallTypeData();
				hall.setGameType(model.getGameType());
				hall.setOpenTime(model.getOpenTime().getTime());
				hall.setIcon(model.getIcon());
				hall.setTip(model.getTip());
				hall.setOpen(model.isOpen());

				// 是否在白名單  && (判斷遊戲是否存在白名單中)
				boolean changeByWhiteList = inWhiteList;
				//IP是否在 allowIpList 裡面
				boolean chanegeByAllowIpList = GameUtility.isHaveString(model.getAllowIpList()) && GameUtility.isHaveString(playerIP) && model.getAllowIpList().contains(playerIP);
				
				if (changeByWhiteList || chanegeByAllowIpList) {
					hall.setOpen(true);
					hall.setIcon(GameHallConfigModel.ICON_GAME);
				}
				
				response.addData(hall);
			}
		}
		
		if (GameUtility.getMapSize(hallActivitySettings) > 0) {
			for (Entry<String, HashMap<String, GameActivitySettingModel>> gameSettings : hallActivitySettings.entrySet()) {
				String gameType = gameSettings.getKey();
				HashMap<String, GameActivitySettings> setting = new HashMap<String, GameActivitySettings>();
				for (Entry<String, GameActivitySettingModel> hallSettings : gameSettings.getValue().entrySet()) {
					String hall = hallSettings.getKey();
					GameActivitySettingModel model = hallSettings.getValue();
					GameActivitySettings settings = new GameActivitySettings();
					settings.setActivityTimes(model.getNormalTimeSetting());
					settings.setExtraActivityTimes(settings.getExtraActivityTimes());
					setting.put(hall, settings);
				}
				response.getGameActivitySettings().put(gameType, setting);
			}
		}
		 
		response.setErrorCode(ErrorCode.SUCCESS);
		return response;
	}
	
	/**
	 * 讀取大廳遊戲列表
	 */
	public void onGetHallTypeList(Player player, GUID sessionid) {
		String playerid = player.getGuid().toString();
		GameListWhileGroup group = ((GameLobbyModule) Contexts.getModule()).getWhileGroup();
		SLobbyHallListResponse response = new SLobbyHallListResponse();
		String PlayerIP = PlayerCommonInfoManager.getInstance().getClientIpBySessionId(sessionid);

		/*
		// 判斷玩家是哪個OS來的
		OSType osType = null;
		switch (player.getUserType()) {
		// 一般真人玩家
		case PlayerUserType.PLAYER: {
			String platformTypeStr = PlayerCommonInfoManager.getInstance()
					.getLoginPlatformBySessionId(sessionid);
			PlatformType platformType = PlatformType.getENUMbyString(platformTypeStr);
			if (platformType != null) {
				osType = OSType.getOSTypeByPlatformType(platformType);
			} else {
				if (log.isErrorEnabled()) {
					log.error("can't recognize login platform, playerGUID: " + player.getGuid().toString() + ", accountID: " + player.getAccountId().toString());
				}
				response.setErrorCode(ErrorCode.HALL_DATA_NOTFIND);
				MessageAction.sendMessage(sessionid, MessageTypes.S_LOBBY_HALLTYPELIST_RESPONSE, response);
				return;
			}
			break;
		}
		// 小樂一律提供PC版的gameList
		case PlayerUserType.ROBORT: {
			osType = OSType.ANDROID;
			break;
		}
		// 新的PlayerUserType就要在這邊加判斷(case...)，否則就會提供全部的gameList
		default:
			break;
		}*/
		
		
		OSType osType = player.getOsType();
		if(osType == null){ //沒有OSTYPE
			response.setErrorCode(ErrorCode.HALL_DATA_NOTFIND);
			MessageAction.sendMessage(sessionid, MessageTypes.S_LOBBY_HALLTYPELIST_RESPONSE, response);
		}
		response = getHallTypeList(group, playerid, PlayerIP, osType);
		
		/*
		boolean inWhiteList = group.hasWhile(playerid);

		for (GameHallConfigModel model : gameList) {
			//只提供與玩家OS相同的列表
			if(osType != null && osType != model.getOsType()){
				continue;
			}
			
			SHallTypeData hall = new SHallTypeData();
			hall.setGameType(model.getGameType());
			hall.setOpenTime(model.getOpenTime().getTime());
			hall.setIcon(model.getIcon());
			hall.setTip(model.getTip());
			hall.setOpen(model.isOpen());

			// 是否在白名單  && (判斷遊戲是否存在白名單中)
			boolean changeByWhiteList = inWhiteList;
			//IP是否在 allowIpList 裡面
			boolean chanegeByAllowIpList = GameUtility.isHaveString(model.getAllowIpList()) && GameUtility.isHaveString(PlayerIP) && model.getAllowIpList().contains(PlayerIP);
			
			if (changeByWhiteList || chanegeByAllowIpList) {
				hall.setOpen(true);
				hall.setIcon(GameHallConfigModel.ICON_GAME);
			}
			
			response.addData(hall);
		}
		
		for (Entry<String, HashMap<String, GameActivitySettingModel>> gameSettings : hallActivitySettings.entrySet()) {
			String gameType = gameSettings.getKey();
			HashMap<String, GameActivitySettings> setting = new HashMap<String, GameActivitySettings>();
			for (Entry<String, GameActivitySettingModel> hallSettings : gameSettings.getValue().entrySet()) {
				String hall = hallSettings.getKey();
				GameActivitySettingModel model = hallSettings.getValue();
				GameActivitySettings settings = new GameActivitySettings();
				settings.setActivityTimes(model.getNormalTimeSetting());
				settings.setExtraActivityTimes(settings.getExtraActivityTimes());
				setting.put(hall, settings);
			}
			response.getGameActivitySettings().put(gameType, setting);
		}
		
		response.setErrorCode(ErrorCode.SUCCESS);
		*/
		MessageAction.sendMessage(sessionid, MessageTypes.S_LOBBY_HALLTYPELIST_RESPONSE, response);
		
	}
	
	/**
	 * 確認玩家離線保留資料
	 * @param request
	 */
	public static void onCheckPlayerOfflineKeepData(GameLobbyAction action)
	{
		Player player = action.getPlayer();
		GUID sessionid = action.getSessionID(); 
		
//		if(log.isDebugEnabled())
//		{
			//log.debug("<<onCheckPlayerOfflineKeepData>> name : " + player.getName());
//		}
		
		GameGetKeepSeatAyncCommand aync = new GameGetKeepSeatAyncCommand();
		aync.methodType = DCLiteMessageType.LOBBY_GET_OFFLINEKEEPDATA;
		aync.datatype = GameGetKeepSeatAyncCommand.TYPE_ALL;
		aync.playerGUID = player.getGuid().toString();
		aync.key = player.getGuid();
		action.getModule().addCentralAsyncCommand(aync, sessionid , player);
	}
	
	/**
	 * 確認玩家離線保留資料BACK
	 * @see #onCheckPlayerOfflineKeepData
	 */
	public static void onCheckPlayerKeepDataBack(ArrayList<GameKeepSeatData> list, Object... userObject) {
		GUID sessionid = (GUID) userObject[0];
		Player player = (Player) userObject[1];


		SPlayerCheckOfflineKeepDataResponse response = new SPlayerCheckOfflineKeepDataResponse();
		ArrayList<SimpleKeepData> keep = new ArrayList<SimpleKeepData>();
		for (GameKeepSeatData dd : list) {
			
			String gameType = dd.getGameName();
			String hallType = dd.getHalltype();
			int machineID = dd.getMachineid();
			
			SimpleKeepData data = new SimpleKeepData();
			data.game = gameType;
			data.halltype = hallType;
			data.machineid = machineID;
			data.seatid = dd.getSeatid();
			data.stat = dd.getMachinestat();
			data.overtime = dd.getTimeout();

			//嘗試尋找這個座位應該是哪一個gainRateID
			if(GainRateSystem.isAffectGame(gameType)){
				
				boolean isFoundGainRateID = false;
				
				ArrayList<GainRateData> gainRateDatas = GainRateSystem.getGainRateList(gameType, hallType);
				if(gainRateDatas != null){
					for(GainRateData gainRateData : gainRateDatas){
						//是這個比值的機台號碼
						if(machineID >= gainRateData.getBeginMachineID() && machineID <= gainRateData.getEndMachineID()){
							data.gainRateID = gainRateData.getId();
							isFoundGainRateID = true;
							break;
						}
					}
				}
				
				//沒找到的話，就提供預設的吧
				if(!isFoundGainRateID){
					//這麼做是因為在比值系統之前所使用的機台編號，剛好不在新規則的數字範圍內
					//而預設的ID理論上是1:1的機台(也就是已存在的機台)
					data.gainRateID = GainRateSystem.getDefaultGainRateID(gameType, hallType);
				}
			}
			else{
				data.gainRateID = GainRateSystem.SYSTEM_DEFAULT_GAIN_RATE_ID;
			}
			
			keep.add(data);
		}

		response.setData(keep);
		response.setErrorCode(ErrorCode.SUCCESS);
		MessageAction.sendMessage(sessionid, MessageTypes.S_PLAYER_CHECK_OFFLINEKEEPDATA_RESPONSE, response);
	}
	
	
	
	/**
	 * 更新玩家離線保留資料
	 * @param request
	 */
	public static void onModifyPlayerOfflineKeepData(CPlayerModifyOfflineKeepDataRequest request , GameLobbyAction action)
	{
		Player player = action.getPlayer();
		GUID sessionid = action.getSessionID();
		SimpleKeepData data = request.getData();
		String game = data.game;
		
		if(!request.isGo())
		{
					// 尋找doorman
					ArrayList<GameTypeInfo> infolist = BasicSetting.getInstance().getGameTypeInfoList();
					GameTypeInfo remote = null;
					for (GameTypeInfo info : infolist) 
					{
						if (info.getGameType().equals(game)) {
							remote = info;
							break;
						}
					}
					
					
					/**不前往，通知game doorman刪除**/
					SDoorManCancelOfflineKeepDataRequest content = new SDoorManCancelOfflineKeepDataRequest();
					content.setData(data);
					content.setSessionid(sessionid);
					content.setPlayerGUID(player.getGuid());
					
					
					GroupMessage msg = new GroupMessage();
					msg.setMessageType(MessageTypes.S_DOORMAN_CANCEL_OFFLINEKEEPDATA);
					msg.setMessageContent(content);
					msg.setSessionID(content.getSessionid());
					msg.setDestModuleID(remote.getModuleId());
					msg.setSrcService(action.getModuleID());
					MessageAction.sendGroupMessage(msg);
		}
		//直接前往，後續doorman處理
		else
		{
			SPlayerModifyOfflineKeepDataResponse response = new SPlayerModifyOfflineKeepDataResponse();
			response.setErrorCode(ErrorCode.SUCCESS);
			response.setGo(request.isGo());
			MessageAction.sendMessage( sessionid, MessageTypes.S_PLAYER_MODIFY_OFFLINEKEEPDATA_RESPONSE , response);
		}
	}
	
	
	/**
	 * 更新玩家離線保留資料BACK
	 * @see #onModifyPlayerOfflineKeepData
	 */
	public static void onModifyPlayerOfflineKeepDataBack(SDoorManCancelOfflineKeepDataBackRequest request ,GameLobbyAction action)
	{
		SPlayerModifyOfflineKeepDataResponse response = new SPlayerModifyOfflineKeepDataResponse();
		response.setErrorCode(request.getErrorCode());
		MessageAction.sendMessage( request.getSessionid(), MessageTypes.S_PLAYER_MODIFY_OFFLINEKEEPDATA_RESPONSE , response);
	}
	
	/**
	 * 	取得大廳即時在線人數
	 */
	public static ArrayList<GUID> getHallUserNumber(){
		GameLobbyModule module = (GameLobbyModule) Contexts.getModule();
		HashMap<GUID, Player> playerlist = module.getZone().getPlayerList();
		ArrayList<GUID> list = new ArrayList<GUID>();
		for(GUID id:playerlist.keySet()){
			list.add(id);
		}
		return list;
	}
	
	/**
	 * GMTool修改白名單
	 */
	public static void editGameWhiteList(WhiteListInfo info){
		GameLobbyModule module = (GameLobbyModule) Contexts.getModule();
		if(module == null){
			if (log.isErrorEnabled()) {
				log.error("get module fail, module = null");
			}
			return;
		}
		GameListWhileGroup group = module.getWhileGroup();
		if(group == null){
			GMToolApiMultiModuleDAO.setProcessResult(module.getModuleName(), info.getResultKey(), false, "false");
			return;
		}
		testCheckWhileList();

		if (info.isDeny()) {
			if(group.getGroup().containsKey(info.getPlayerid())){			
				group.getGroup().remove(info.getPlayerid());
				testCheckWhileList();
			}
			GMToolApiMultiModuleDAO.setProcessResult(module.getModuleName(), info.getResultKey(), true, null);
			return;
		}
		
		GameListWhileData data;
		if(group.getGroup().containsKey(info.getPlayerid())){			
			data = group.getGroup().get(info.getPlayerid());
			data.expiredtime = info.getExpired();
			data.iconlist = group.strToList(info.getAllowicons());
			data.list = group.strToList(info.getAllowlist());
		}else{
			data = new GameListWhileData();
			data.playerid = info.getPlayerid();
			data.expiredtime = info.getExpired();
			data.iconlist = group.strToList(info.getAllowicons());
			data.list = group.strToList(info.getAllowlist());
			
			group.getGroup().put(info.getPlayerid(), data);
		}
		testCheckWhileList();
		
		GMToolApiMultiModuleDAO.setProcessResult(module.getModuleName(), info.getResultKey(), true, null);
	}
	
	public static void testCheckWhileList(){
		GameLobbyModule module = (GameLobbyModule) Contexts.getModule();
		if(module == null){
			System.out.println("test module not found!! ~~~");
			return;
		}
		GameListWhileGroup group = module.getWhileGroup();
		if(group == null){
			System.out.println("test group not found!! ~~~");
			return;
		}
		ConcurrentHashMap<String,GameListWhileData> gg = group.getGroup();
		System.out.println("now ConcurrentHashMap ------------");
		for(String sKey:gg.keySet()){
			System.out.println(sKey + "-----");
			GameListWhileData data = gg.get(sKey);
			System.out.println(data.playerid);
			System.out.println(data.expiredtime);
			System.out.println(data.iconlist);
			System.out.println(data.list);
		}
	}
	
	
	public static void getBulletinTitleCount(long lastModifyTime , GUID sessionId){
		GameLobbyModule module = (GameLobbyModule) Contexts.getModule();		
		if(module == null){
			//找不到module回傳錯誤
			MessageAction.sendMessage(sessionId, MessageTypes.S_BULLETIN_LIST, "");
			return;
		}
		HashMap<Integer, BulletinModelInfo> bulletinMap = module.getBulletinMap();
		int newSystemCount = 0;
		int newEventCount = 0;
		for(BulletinModelInfo model:bulletinMap.values()){
			long bulletinModifyTime = model.getModifyTime();
			if(lastModifyTime >= bulletinModifyTime){
				// 舊的不累加
				continue;
			}
			switch (model.getType()) {
			case 0:
				//'0' 系統公告		
				newSystemCount++;
				break;
			case 1:
				//'1' 活動公告
				newEventCount++;
				break;
			default:
				break;
			}
		}
	}
	
	
	/**
	 * 取得公告標題列表  (依據最後修改時間)只取有更新的
	 */
	public static void getBulletinTitleList(long lastModifyTime , GUID sessionId){
		GameLobbyModule module = (GameLobbyModule) Contexts.getModule();
		BulletinListInfo bulletinListInfo = new BulletinListInfo();
		
		if(module == null){
			//找不到module回傳錯誤
			bulletinListInfo.setErrorCode(ErrorCode.DATA_NOT_FOUND);
			MessageAction.sendMessage(sessionId, MessageTypes.S_BULLETIN_LIST, bulletinListInfo);
			return;
		}
		
		HashMap<Integer, BulletinModelInfo> bulletinMap = module.getBulletinMap();
		
		for(BulletinModelInfo model:bulletinMap.values()){
			long bulletinModifyTime = model.getModifyTime();
			long endTime = model.getEndTime();
			
			if(lastModifyTime >= bulletinModifyTime){
				// 舊的不更新
				continue;
			}
			
			BulletinTitleInfo info = new BulletinTitleInfo();
			info.setStartTime(model.getStartTime());
			info.setTitle(model.getTitle());
			info.setStatus(model.getStatus());
			info.setBulletinId(model.getId());
			info.setType(model.getType());
			info.setEndTime(model.getEndTime());
			
			// 如果是已過期就設定狀態告知
			if(endTime < TimeSys.currentTimeSec()){
				info.setStatus(2);
			}
			
			bulletinListInfo.getBulletinList().add(info);
		}
		bulletinListInfo.setLastModifyTime(TimeSys.currentTimeSec());
		bulletinListInfo.setErrorCode(ErrorCode.SUCCESS);
		//傳資料給client
		MessageAction.sendMessage(sessionId, MessageTypes.S_BULLETIN_LIST, bulletinListInfo);
	}
	
	/**
	 * 取得公告內容 (依據公告編號)
	 */
	public static void getBulletinContent(int bulletinNumber , GUID sessionId){
		GameLobbyModule module = (GameLobbyModule) Contexts.getModule();
		BulletinInfo bulletinInfo = new BulletinInfo();
		if(module == null){
			//找不到module回傳錯誤
			bulletinInfo.setErrorCode(ErrorCode.DATA_NOT_FOUND);
			MessageAction.sendMessage(sessionId, MessageTypes.S_BULLETIN_CONTENT, bulletinInfo);
			return;
		}
		HashMap<Integer, BulletinModelInfo> bulletinMap = module.getBulletinMap();
		
		if(!bulletinMap.containsKey(bulletinNumber)){
			//找不到該筆公告回傳錯誤
			bulletinInfo.setErrorCode(ErrorCode.DATA_NOT_FOUND);
			MessageAction.sendMessage(sessionId, MessageTypes.S_BULLETIN_CONTENT, bulletinInfo);
			return;
		}
		BulletinModelInfo bulletinModelInfo = bulletinMap.get(bulletinNumber);
		
		
		bulletinInfo.setErrorCode(ErrorCode.SUCCESS);
		bulletinInfo.setId(bulletinModelInfo.getId());
		bulletinInfo.setTitle(bulletinModelInfo.getTitle());
		bulletinInfo.setContent(bulletinModelInfo.getContent());
		bulletinInfo.setStartTime(bulletinModelInfo.getStartTime());
		bulletinInfo.setEndTime(bulletinModelInfo.getEndTime());
		
		//傳資料給client
		MessageAction.sendMessage(sessionId, MessageTypes.S_BULLETIN_CONTENT, bulletinInfo);
	}
	
	/**
	 * 更新公告暫存 (透過GMTool通知) 
	 */
	public static void updateBulletin(BulletinModelInfo model){
		GameLobbyModule module = (GameLobbyModule) Contexts.getModule();
		System.out.println("GMTool update server bulletin list !!~~~~~");
		if(module == null){
			// 找不到module回傳錯誤
			GMToolApiMultiModuleDAO.setProcessResult(module.getModuleName(), model.getResultKey(), false, "false");
			return;
		}
		int id = model.getId();
		module.getBulletinMap().put(id,model);

		if(model.getStatus() == 0 || model.getEndTime() <= TimeSys.currentTimeSec())
		{
			GMToolApiMultiModuleDAO.setProcessResult(module.getModuleName(), model.getResultKey(), true, null);
			
			//如果是關閉或是過期的就不送紅點
			return;
		}

		// 是否需要執行廣播
		boolean runBroadcast = model.getBroadcastModuleId().equals(module.getModuleName());

		GMToolApiMultiModuleDAO.setProcessResult(module.getModuleName(), model.getResultKey(), true, null);
		return;
	}
	
	/**
	 * 從DB更新整個公告清單 (透過GMTool通知)<br>
	 * 跟 初始化時的 updateBulletinList 分開原因是把撈DB的動做放在GMTool做，不要卡在server這邊
	 */
	public static void updateAllBulletinListByGMTool(GameLobbyModule module,BullentinAllListInfo info){
		System.out.println("update server all bulletin list !!~~~~~");
		module.setBulletinMap(info.getBulletinMap());
		GMToolApiMultiModuleDAO.setProcessResult(module.getModuleName(), info.getResultKey(), true, null);
	}
	
	/**
	 * 從DB更新整個公告清單 (初始化時做)
	 */
	public static boolean updateBulletinList(GameLobbyModule module){
		System.out.println("init update bulletin list !!~~~~~");
		HashMap<Integer, BulletinModelInfo> bulletinMap = BulletinDAO.getBulletinInfoMap();		
		module.setBulletinMap(bulletinMap);
		return true;
	}
	
	/**
	 * 處理 client 要求遊戲館的座位列表
	 * 
	 * @param request
	 */
	public static void handleHallMachinedataRequest(Player player, CLobbyHallMachinedataRequest request) {
		if (player == null) {
			return;
		}

		GUID sessionId = player.getSessionID();
		String gameType = request.getGamename();
		String hallType = request.getHalltype();
		int gainRateID = request.getGainRateID();
		int level = player.getLevel();
		int VIPLevel = player.getVip();
		
		// game type
		if (!GameUtility.isHaveString(gameType)) {
			MessageAction.sendMessage(sessionId, MessageTypes.S_LOBBY_HALL_MACHINEDATA_RESPONSE,
					ErrorCode.INVALID_GAME_NAME);
			return;
		}
		
		
		// 取出指定的遊戲資訊
		GameTypeInfo info = BasicSettingUtil.getGameTypeInfo(gameType);
		if (info == null) {
			MessageAction.sendMessage(sessionId, MessageTypes.S_LOBBY_HALL_MACHINEDATA_RESPONSE,
					ErrorCode.INVALID_GAME_NAME);
			return;
		}

		// 館別
		if(info.getIsTableGame() ==GAME_SLOT )
		{
			if (!GameUtility.isHaveString(hallType)) {
					MessageAction.sendMessage(sessionId, MessageTypes.S_LOBBY_HALL_MACHINEDATA_RESPONSE,
							ErrorCode.INVALID_HALL_TYPE);
					return;
			}
		}
	
		// 套用幣值比設定的遊戲才進行條件判斷
		if(GainRateSystem.isAffectGame(gameType)){
			// 該玩家是否允許訪問這種比值的機台
			if(!GainRateSystem.isPlayerCanPlay(level, VIPLevel, gameType, hallType, gainRateID, true)){
				MessageAction.sendMessage(sessionId, MessageTypes.S_LOBBY_HALL_MACHINEDATA_RESPONSE,
						ErrorCode.GAIN_RATE_SYSTEM_LIMIT_LEVEL);
				return;
			}	
		}

		// 取得指定遊戲的 doorman module id
		String doorManModuleId = info.getModuleId();

		// 補上其他資訊
		request.setSessionId(sessionId.toString());
		request.setUserType(player.getUserType());

		GroupMessage msg = new GroupMessage();
		msg.setMessageType(MessageTypes.S_LOBBY_DM_HALL_MACHINEDATA_REQUEST);
		msg.setMessageContent(request);
		msg.setDestModuleID(doorManModuleId);

		MessageAction.sendMessage(msg);
		
		//是玩家才寫log
		if(log.isInfoEnabled() && player.isPlayer()){
			StringBuilder builder = new StringBuilder();
			builder.append("[GetHallMachinedata]");
			builder.append("PlayerId=");
			builder.append(player.getGuid().toString());
			builder.append("|DoorManId=");
			builder.append(doorManModuleId);
			log.info(builder.toString());
		}
	}

	/**
	 * 處理 client 指定要收哪一頁的座位更新資訊
	 * 
	 * @param request
	 */
	public static void handleSetRecvSeatUpdatePageRequest(Player player,
			CSlotCommonDmSetRecvSeatUpdatePageRequest request) {
		if (player == null) {
			return;
		}

		GUID sessionId = player.getSessionID();

		// game type
		if (!GameUtility.isHaveString(request.getGamename())) {
			return;
		}

		// 取出指定的遊戲資訊
		GameTypeInfo info = BasicSettingUtil.getGameTypeInfo(request.getGamename());
		if (info == null) {
			return;
		}

		// 館別
		if (info.getIsTableGame() == GAME_SLOT) {
			if (!GameUtility.isHaveString(request.getHalltype())) {
				return;
			}
		}

		// 取得指定遊戲的 doorman module id
		String doorManModuleId = info.getModuleId();

		GroupMessage msg = new GroupMessage();
		msg.setMessageType(MessageTypes.C_SLOT_COMMON_DM_SET_RECV_SEAT_UPDATE_PAGE_REQUEST);
		msg.setMessageContent(request);
		msg.setSessionID(sessionId);
		msg.setDestModuleID(doorManModuleId);

		MessageAction.sendMessage(msg);
	}

	/**
	 * 從GMTool取得跑馬燈公告資訊 
	 */
	public static void getGMToolMarqueeList(GUID sessionId , long lastModifyTime){
		GroupMessage gm = new GroupMessage();
		gm.setDestModuleID(ModuleName.GMTOOL);
		gm.setMessageType(MessageTypes.S_GMTOOL_MARQUEE_LIST);
		gm.setMessageContent(lastModifyTime);
		gm.setSessionID(sessionId);
		MessageAction.sendGroupMessage(gm);
	}
	
	/**
	 * 從GMTool取得強制公告資訊 
	 */
	public static void getGMToolForciblyList(GUID sessionId){
		GroupMessage gm = new GroupMessage();
		gm.setDestModuleID(ModuleName.GMTOOL);
		gm.setMessageType(MessageTypes.S_GMTOOL_FORCIBLY_LIST);
		gm.setSessionID(sessionId);
		MessageAction.sendGroupMessage(gm);
	}
	
	/**
	 * client 要求離線 spin 結果
	 * 
	 * @param player
	 */
	public void recvOfflineSpinResultGetRequest(Player player) {
		if (player == null) {
			return;
		}

		LoadOfflineSpinResultAsync task = new LoadOfflineSpinResultAsync();
		task.setPlayerId(player.getGuid());
		task.setSessionId(player.getSessionID());
		task.key = player.getGuid();

		getOwnerModule().addAsyncCommand(task);
	}
	
	
	@Override
	public void onPlayerLogin(Player player){
		super.onPlayerLogin(player);
		AnalysisProcessor.sendPlayerEnterLobby(player, PlayerEnterSeatStatusType.ENTER_SEAT_SUCCESS);
	}
	
	@Override
	public Player removePlayerDataFromZone(GUID playerGUID, boolean isFromSessionCloseNotify){
		Player player = super.removePlayerDataFromZone(playerGUID, isFromSessionCloseNotify);
		if(player != null)
			AnalysisProcessor.sendPlayerEnterLobby(player, PlayerEnterSeatStatusType.LEAVE_SEAT);
		
		return player;
	}
	
	/**
	 * 從 db 載入遊戲設定後觸發
	 * 
	 * @param activitySettings
	 */
	public void onLoadActivitySetting(ArrayList<GameActivitySettingModel> activitySettings) {
		// 沒資料就直接結束
		if (GameUtility.getArrayListSize(activitySettings) == 0) {
			return;
		}

		hallActivitySettings.clear();
		
		HashMap<String, GameActivitySettingModel> hallActivityMap = new HashMap<String, GameActivitySettingModel>();
		for (GameActivitySettingModel model : activitySettings) {
			hallActivityMap.put(model.getHallType(), model);
			hallActivitySettings.put(model.getGame(), hallActivityMap);
		}
	}
	
}
